using System;
using System.Collections;
using System.Collections.Generic;
using JufGame.AI;
using UnityEngine;

namespace JufGame
{
	public enum NodeType
	{
		Normal, Obstacles,
	}
	public class MyOctreeNode:IAStarNode<MyOctreeNode>, IComparable<MyOctreeNode>
	{
		private const float MIN_CUBE_SIZE = 1f; // 最小方格尺寸
		public MyOctreeNode Parent{ get; set; } //父节点
        public float SelfCost { get; set; }
        public float GCost { get; set; }
        public float HCost { get; set; }
        public float FCost => GCost + HCost;

        public MyOctreeNode[] Children; //子节点
		public Bounds NodeCube; //用包围盒作为结点方块，方便后续检测
		public NodeType Type = NodeType.Normal;
		public MyOctreeNode(Bounds nodeCube, MyOctreeNode parent)
		{
			Parent = parent;
			NodeCube = nodeCube;
			SelfCost = 1;
		}
		public void Divide(Collider collider)
		{
			//因为是正方体，所以用一条边来判断尺寸即可
			if(NodeCube.size.x >= MIN_CUBE_SIZE) 
			{
				// 子方块的半尺寸， 用半尺寸是因为构建Bounds需要
				float childHalfSize = NodeCube.size.x / 4;
				if (Children == null)
					Children = new MyOctreeNode[8];
				Vector3 offset; //子节点偏移
				for(int i = 0; i < 8; ++i)
				{
					//0~7的二进制位结构恰好满足我们所需要的组合形式
					offset.x = (1 & i) != 0 ? childHalfSize : -childHalfSize; //取二进制第0位
					offset.y = (2 & i) != 0 ? childHalfSize : -childHalfSize; //取二进制第1位
					offset.z = (4 & i) != 0 ? childHalfSize : -childHalfSize; //取二进制第2位
					var childBounds = new Bounds(NodeCube.center + offset, 2 * childHalfSize * Vector3.one);
					if(Children[i] == null)
						Children[i] = new MyOctreeNode(childBounds, this);
					/*
					进一步分裂前，先判断一下有没有遇到障碍物，没有就不要继续分裂了;
					也可以再附带添加些其它检测条件，比如obj.layer等
					*/
					if(childBounds.Intersects(collider.bounds))
					{
						Children[i].Divide(collider); // 每个子节点继续分裂
					}
				}
			}
			else
			{
				Type = NodeType.Obstacles;
			}
		}
		//seeOne为true，则只查看分裂后的一个，否则查看所有分裂后的方块
		public void Draw(bool isSeeOne)
		{
			var drawColor = Color.green;
			if(Type == NodeType.Obstacles)
				drawColor = Color.red;
			Gizmos.color = drawColor;
			Gizmos.DrawWireCube(NodeCube.center, NodeCube.size);
			if (Children == null)
				return;
			foreach(var c in Children)
			{
				c.Draw(isSeeOne);
				if(isSeeOne)
				{
					break;
				}
			}
		}

        public float GetDistance(MyOctreeNode otherNode)
        {
			return Vector3.Distance(NodeCube.center, otherNode.NodeCube.center);
        }

        public List<MyOctreeNode> GetSuccessors(object nodeMap)
        {
			var map = (MyGraph<MyOctreeNode, int>)nodeMap;
			return map.GetNeighbor(this);
		}

        public int CompareTo(MyOctreeNode other)
        {
        	float res = FCost - other.FCost;
			if(res == 0)
				res = HCost - other.HCost;
			return (int)res;
        }
    }
}
